package com.spicesoft.clientobjects.swing;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.swing.ListModel;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;

import com.spicesoft.clientobjects.BeanEnhancer;
import com.spicesoft.clientobjects.core.collections.ObservableList;
import com.spicesoft.clientobjects.core.collections.VariableLengthIndexedPropertyChangeEvent;

/**
 * An implementation of {@link ListModel} which works with {@link ObservableList}. An
 * {@link ObservableList} can be obtained by enhancing a regular {@link List} using
 * {@link BeanEnhancer}. An instance of this class "observes" changes made in a list
 * of objects and notifies all {@link ListDataListener}s.
 */
public class SimpleListModel implements ListModel, PropertyChangeListener {

  private Set<ListDataListener> listeners;

  private ObservableList<?> olist;

  public SimpleListModel(List<?> list) {
    if (list instanceof ObservableList) {
      this.olist = (ObservableList<?>) list;
    } else {
      this.olist = new ObservableList(list);
    }
    
    olist.addPropertyChangeListener(this);
  }

  public void addListDataListener(ListDataListener l) {
    if (this.listeners == null) {
      this.listeners = new HashSet<ListDataListener>();
    }
    this.listeners.add(l);
  }

  public Object getElementAt(int i) {
    return olist.get(i);
  }

  public int getSize() {
    return olist.size();
  }

  public void removeListDataListener(ListDataListener l) {
    this.listeners.remove(l);
  }

  public void propertyChange(PropertyChangeEvent evt) {
    VariableLengthIndexedPropertyChangeEvent vevt = (VariableLengthIndexedPropertyChangeEvent) evt;
    for (ListDataListener l : this.listeners) {
      ListDataEvent levt;
      
      switch (vevt.getEventType()) {
      case DELETE:
        if (vevt.getStartIndex() >= 0 && vevt.getEndIndex() >= 0) {
          levt = new ListDataEvent(vevt.getSource(),
              ListDataEvent.INTERVAL_REMOVED, vevt.getStartIndex(), vevt.getEndIndex());
          l.intervalRemoved(levt);
        } else {
          levt = new ListDataEvent(vevt.getSource(),
              ListDataEvent.CONTENTS_CHANGED, vevt.getStartIndex(), vevt.getEndIndex());
          l.contentsChanged(levt);
        }
        break;
      case INSERT:
        levt = new ListDataEvent(vevt.getSource(),
            ListDataEvent.INTERVAL_ADDED, vevt.getStartIndex(), vevt.getEndIndex());
        l.intervalAdded(levt);
        break;
      case MODIFY:
        levt = new ListDataEvent(vevt.getSource(),
            ListDataEvent.CONTENTS_CHANGED, vevt.getStartIndex(), vevt.getEndIndex());
        l.contentsChanged(levt);
        break;
      default:
        throw new RuntimeException("Unknown event type " + vevt.getEventType());
      }
    }
  }

}
